﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Net;
using System.Timers;

namespace VLCMultyRemote
{
    public class Client : INotifyPropertyChanged
    {
        /// <summary>
        /// is the name of the property who needs to be set
        /// is null if no property need to be set
        /// </summary>
        private string waitingForInformation = null;

        /// <summary>
        /// represents the connection to the client
        /// </summary>
        private Connection connection;

        /// <summary>
        /// prepare client for communication
        /// </summary>
        /// <param name="ipEndPoint">IPEndPoint to the client</param>
        /// <param name="name">can be null, is not required fpr communication</param>
        public Client(IPEndPoint ipEndPoint, string name)
        {
            connection = new Connection(ipEndPoint, this);
            this.port = ipEndPoint.Port;
            this.ipAddress = ipEndPoint.Address;
            this.name = name;
        }

        #region public informations

        private string name;

        /// <summary>
        /// gets and sets the name of the client (is not required for the communicaation)
        /// </summary>
        public string Name
        {
            get { return name; }
            set 
            { 
                name = value;
                notifyPropertyChanged("Name");
            }
        }

        private int port;

        /// <summary>
        /// gets the port of the connection
        /// </summary>
        public int Port
        {
            get { return port; }
        }

        private IPAddress ipAddress;

        /// <summary>
        /// gets the IPAdress of the connection
        /// </summary>
        public IPAddress IpAddress
        {
            get { return ipAddress; }
        }

        private bool connected = false;

        /// <summary>
        /// gets and sets the connected status
        /// also sart and stop  the "get played time" routine
        /// </summary>
        public bool Connected
        {
            get { return connected; }
            set 
            { 
                connected = value;
                notifyPropertyChanged("Connected");
                if (value)
                {
                    startGetTimeTimer();
                }
                else
                {
                    stopGetTimeTimer();
                }
            }
        }

        private int timePlayed;

        /// <summary>
        /// gets and sets the current played time
        /// </summary>
        public int TimePlayed
        {
            get { return timePlayed; }
            set 
            { 
                timePlayed = value;
                notifyPropertyChanged("TimePlayed");
            }
        }

        private string title;

        /// <summary>
        /// gets and sets the current title of the video
        /// </summary>
        public string Title
        {
            get { return title; }
            set
            {
                title = value;
                notifyPropertyChanged("Title");
            }
        }

        private string status = "End";

        /// <summary>
        /// gets and sets the status of the client
        /// default: "End"
        /// possible values: End, Pause, Play
        /// </summary>
        public string Status
        {
            get { return status; }
            set 
            { 
                status = value;
                notifyPropertyChanged("Status");
            }
        }


        private bool canControl = true;

        /// <summary>
        /// gets and sets the right to control other clients of the client
        /// </summary>
        public bool CanControl
        {
            get { return canControl; }
            set 
            { 
                canControl = value;
                notifyPropertyChanged("CanControl");
            }
        }

        public delegate void MessageRecievingEventHandler(object sender, String message);

        /// <summary>
        /// event for statusmessages of the connection
        /// </summary>
        public event MessageRecievingEventHandler OnStatusChanged;

        /// <summary>
        /// event for incomming messages
        /// </summary>
        public event MessageRecievingEventHandler OnDataRecievingControl;

        #endregion


        #region timer

        /// <summary>
        /// timer for requesting actual playing time
        /// </summary>
        private Timer requestPlayingTimeTimer;

        private void stopGetTimeTimer()
        {
            requestPlayingTimeTimer.Stop();
            requestPlayingTimeTimer.Close();
            requestPlayingTimeTimer = null;
        }

        private void startGetTimeTimer()
        {
            requestPlayingTimeTimer = new Timer();
            requestPlayingTimeTimer.Interval = 1000;
            requestPlayingTimeTimer.Elapsed += new ElapsedEventHandler(requestPlayingTime);

            requestPlayingTimeTimer.Start();
        }


        void requestPlayingTime(object sender, ElapsedEventArgs e)
        {
            waitingForInformation = "Time";
            connection.sendCommand("get_time\n");
        }

        #endregion

        #region INotifyPropertyChanged
        /// <summary>
        /// implementation of INotifyPropertyChanged need for bindings
        /// </summary>

        public event PropertyChangedEventHandler PropertyChanged;

        private void notifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion

        /// <summary>
        /// starts communication
        /// </summary>
        public void startConnection()
        {
            connection.OnDataRecievingControl += new Connection.MessageRecievingEventHandler(connection_OnDataRecievingControl);
            connection.OnStatusChanged += new Connection.MessageRecievingEventHandler(connection_OnStatusChanged);
            connection.startCommunication();
        }

        private void connection_OnStatusChanged(object sender, string message)
        {
            if (OnStatusChanged != null)
            {
                OnStatusChanged(this, message);
            }
        }

        private void connection_OnDataRecievingControl(object sender, string message)
        {
            parseForRequestedInformation(message);

            if (OnDataRecievingControl != null)
            {
                OnDataRecievingControl(this, message);
            }

            if (requestPlayingTimeTimer == null)
                startGetTimeTimer();
        }

        public void stopConnection()
        {
            connection.finishCommunication();
            connection.OnDataRecievingControl -= new Connection.MessageRecievingEventHandler(connection_OnDataRecievingControl);
            connection.OnStatusChanged -= new Connection.MessageRecievingEventHandler(connection_OnStatusChanged);
        }

        /// <summary>
        /// sends a command to the client
        /// </summary>
        /// <param name="command">end with "\n"</param>
        internal void sendCommand(string command)
        {
            parseForRequest(command);

            connection.sendCommand(command);

            if (command != "get_time\n" && requestPlayingTimeTimer != null)
                stopGetTimeTimer();
        }

        private void parseForRequestedInformation(string toParse)
        { 
            switch (toParse)
            {
                case ("status change: ( play state: 4 ): End"):
                    this.Status = "End";
                    break;
                case ("status change: ( pause state: 4 ): End"):
                    this.Status = "End";
                    break;
                case ("status change: ( pause state: 3 ): Pause"):
                    this.Status = "Pause";
                    break;
                case ("status change: ( play state: 3 ): Pause"):
                    this.Status = "Pause";
                    break;
                case ("status change: ( play state: 2 ): Play"):
                    this.Status = "Play";
                    break;
                case ("status change: ( pause state: 2 ): Play"):
                    this.Status = "Play";
                    break;
                case ("status change: ( play state: 3 )"):
                    this.Status = "Play";
                    break;
            }

            switch (waitingForInformation)
            {
                case("Time"):
                    try
                    {
                        this.TimePlayed = int.Parse(toParse);
                    }
                    catch (FormatException) // ignore other informations
                    { }
                    waitingForInformation = null;
                    break;

                case ("Title"):
                    this.Title = toParse;
                    waitingForInformation = null;
                    break;
            }
        
        }

        /// <summary>
        /// sets parsing for information request
        /// </summary>
        /// <param name="toParse"></param>
        private void parseForRequest(string toParse)
        {
            if (waitingForInformation == null)
            {
                switch (toParse)
                {
                    case ("get_title\n"):
                        waitingForInformation = "Title";
                        break;
                }
            }
        }
    }
}
